#include <stdio.h>
#include <liburing.h>
#include <netinet/in.h>
#include <string>
#include <unistd.h>
#include <memory>
#include <string.h>
#include <iostream>
class Myuring
{
public:
    Myuring(int port = 9999) : m_port(port)
    {
        m_str.reserve(1024);
    };
    struct conn_info
    {
        int fd;
        int event;
    };
    enum EventType
    {
        EVENT_ACCEPT,
        EVENT_READ,
        EVENT_WRITE,
    };
    void init()
    {
        m_fd = socket(AF_INET, SOCK_STREAM, 0);
        struct sockaddr_in serveraddr;
        memset(&serveraddr, 0, sizeof(struct sockaddr_in));
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
        serveraddr.sin_port = htons(m_port);

        if (-1 == bind(m_fd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr)))
        {
            perror("bind");
            return;
        }
        listen(m_fd, 10);
        memset(&params, 0, sizeof(params));
        io_uring_queue_init_params(1024, &ring, &params);
        std::cout << "init end:" << std::endl;
        return;
    }

    void run()
    {
        std::cout << "run...." << std::endl;
        set_event_accept();

        while (1)
        {
            io_uring_submit(&ring);
            // std::cout << "run...." << std::endl;
            struct io_uring_cqe *cqe;
            io_uring_wait_cqe(&ring, &cqe);
            struct io_uring_cqe *cqes[128];
            size_t nready = io_uring_peek_batch_cqe(&ring, cqes, 128);
            for (int i = 0; i < nready; i++)
            {
                // struct conn_info *coninfo = (struct conn_info *)cqes[i]->user_data;
                struct conn_info result;
                memcpy(&result, &cqes[i]->user_data, sizeof(struct conn_info));
                // std::cout << "run...." << std::endl;
                if (result.event == EVENT_ACCEPT)
                {
                    int client_fd = cqes[i]->res;
                    std::cout << "accept client fd:" << client_fd << std::endl;
                    set_event_accept();
                    set_event_recv(client_fd);
                }
                else if (result.event == EVENT_READ)
                {
                    std::cout << "read data:" << m_str.c_str() << std::endl;
                    int ret = cqes[i]->res;
                    if (ret == 0)
                    {
                        close(result.fd);
                        std::cout << "close fd:" << result.fd << std::endl;
                    }
                    else
                    {
                        set_event_send(result.fd, ret);
                    }
                }
                else
                {
                    int ret = cqes[i]->res;
                    set_event_recv(result.fd);
                }
            }
            io_uring_cq_advance(&ring, nready);
        }
    }

    void set_event_accept()
    {
        // std::cout << "set accept...." << std::endl;
        struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
        struct conn_info accept_info = {
            .fd = m_fd,
            .event = EventType::EVENT_ACCEPT,
        };
        memcpy(&sqe->user_data, &accept_info, sizeof(struct conn_info));
        io_uring_prep_accept(sqe, m_fd, (struct sockaddr *)&clientaddr, &len, 0);
    }

    void set_event_recv(int client_fd)
    {
        std::cout << "set_event_recv...." << std::endl;
        struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
        struct conn_info recv_info = {
            .fd = client_fd,
            .event = EVENT_READ,
        };
        memcpy(&sqe->user_data, &recv_info, sizeof(struct conn_info));
        io_uring_prep_recv(sqe, client_fd, (void *)m_str.c_str(), m_str.capacity(), 0);
    }

    void set_event_send(int client_fd, int length)
    {
        struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
        struct conn_info send_info = {
            .fd = client_fd,
            .event = EVENT_WRITE,
        };
        memcpy(&sqe->user_data, &send_info, sizeof(struct conn_info));
        io_uring_prep_send(sqe, client_fd, (void *)m_str.c_str(), length, 0);
    }

private:
    int m_port;
    int m_fd;
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);
    std::string m_str;

    struct io_uring_params params;
    struct io_uring ring;
};

int main()
{
    Myuring my;
    my.init();
    my.run();
    return 0;
}